package mysql

import (
	"errors"
	"fmt"
	"gitee.com/captials-team/ubdframe/src/common/utils"
	"gitee.com/captials-team/ubdframe/src/domain/configstc"
	"gitee.com/captials-team/ubdframe/src/domain/dto"
	"gitee.com/captials-team/ubdframe/src/domain/dto/paginate"
	"gitee.com/captials-team/ubdframe/src/domain/interfaces"
	"gitee.com/captials-team/ubdframe/src/domain/models"
	mysqlClients "gitee.com/captials-team/ubdframe/src/infrastructure/clients/mysql"
	"gorm.io/gorm"
)

func NewUserDao(conf configstc.DBConfig) *UserDao {
	return &UserDao{
		db:   mysqlClients.NewGormDB(conf),
		conf: conf,
	}
}

type UserDao struct {
	db   *gorm.DB
	conf configstc.DBConfig
}

func (dao *UserDao) QueryByAccount(account string) (*models.User, error) {
	var info models.User
	ret := dao.db.Model(&info).First(&info, "account = ?", account)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *UserDao) QueryByEmail(query string) (*models.User, error) {
	var info models.User
	ret := dao.db.Model(&info).First(&info, "email = ?", query)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *UserDao) QueryByPhone(query string) (*models.User, error) {
	var info models.User
	ret := dao.db.Model(&info).First(&info, "phone = ?", query)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *UserDao) Search(search *dto.SearchUserParams, pa *paginate.Pager) ([]*models.User, *paginate.Pager, error) {
	if search == nil {
		search = &dto.SearchUserParams{}
	}
	if pa == nil {
		pa = &paginate.Pager{}
	}
	pa.Correct()

	db := dao.db.Model(new(models.User))

	var list []*models.User

	if search.Keywords != "" {
		keywords := "%" + search.Keywords + "%"
		db = db.Where("(nickname like ? OR account like ?)", keywords, keywords)
	}
	if search.Status > 0 {
		db = db.Where("status=?", search.Status)
	}
	db = db.Omit("password", "salt")

	if ret := db.Count(&pa.Total); ret.Error != nil {
		return list, pa, ret.Error
	}

	db = db.Order("id desc")
	if ret := db.Limit(pa.Size).Offset(pa.Offset()).Find(&list); ret.Error != nil {
		return list, pa, ret.Error
	}
	pa.Correct()
	return list, pa, nil
}

func (dao *UserDao) Query(id int64) (*models.User, error) {
	var info models.User
	ret := dao.db.Model(&info).First(&info, "id = ?", id)
	if errors.Is(ret.Error, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if ret.Error != nil {
		return nil, ret.Error
	}
	return &info, nil
}

func (dao *UserDao) Add(add *models.User) (int64, error) {
	if add.Id > 0 {
		return 0, fmt.Errorf("not support update")
	}
	if add.Password == "" {
		add.Password = utils.NewMd5Password("123456", add.Salt)
	}
	ret := dao.db.Model(add).Create(add)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("add fail")
	}

	return add.Id, nil
}

// AddCheckAccount 检查并添加账户
func (dao *UserDao) AddCheckAccount(u *models.User) (int64, error) {
	admin, err := dao.QueryByAccount(u.Account)
	if err != nil {
		return 0, err
	}
	if admin != nil {
		return admin.Id, nil
	}
	return dao.Add(u)
}

func (dao *UserDao) ResetStatus(id int64, status int) (int64, error) {
	// 根据条件更新
	db := dao.db.Model(&models.User{})
	ret := db.Where("id = ?", id).Update("status", status)
	return ret.RowsAffected, ret.Error
}

// Update 更新
func (dao *UserDao) Update(id int64, up *models.User) (int64, error) {
	if id <= 0 {
		return 0, fmt.Errorf("not find info")
	}
	up.Id = id
	//需忽略 password，password 单独处理
	ret := dao.db.Omit("password", "account", "salt").Save(up)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no update")
	}
	return ret.RowsAffected, nil
}

func (dao *UserDao) ResetPassword(id int64, password string) (int64, error) {
	db := dao.db.Model(&models.User{}).Where("id=?", id)

	ret := db.Update("password", password)
	if ret.Error != nil {
		return 0, ret.Error
	}
	return ret.RowsAffected, nil
}

func (dao *UserDao) UpdateProInfo(id int64, up *models.User) (int64, error) {
	if id <= 0 {
		return 0, fmt.Errorf("not find info")
	}
	up.Id = id

	var fields []string
	if len(up.Nickname) > 0 {
		fields = append(fields, "nickname")
	}
	if len(up.Avatar) > 0 {
		fields = append(fields, "avatar")
	}
	if len(up.Email) > 0 {
		fields = append(fields, "email")
	}
	if len(up.Phone) > 0 {
		fields = append(fields, "phone")
	}
	if up.Sex > 0 {
		fields = append(fields, "sex")
	}

	//需忽略password，password单独处理
	ret := dao.db.Select(fields).Save(up)
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no update")
	}
	return ret.RowsAffected, nil
}

// LoginNote 登录记录
func (dao *UserDao) LoginNote(id int64, info *dto.LoginInfoParam) (int64, error) {
	if info == nil {
		return 0, nil
	}
	db := dao.db.Model(&models.User{}).Where("id=?", id)

	ret := db.Updates(map[string]interface{}{
		"last_login": info.Time,
		"last_ip":    info.Ip,
	})
	if ret.Error != nil {
		return 0, ret.Error
	}
	return ret.RowsAffected, nil
}

func (dao *UserDao) Delete(id int64) (int64, error) {
	ret := dao.db.Where("id=?", id).Delete(&models.User{})
	if ret.Error != nil {
		return 0, ret.Error
	}
	if ret.RowsAffected == 0 {
		return 0, fmt.Errorf("no delete")
	}
	return ret.RowsAffected, nil
}

func (dao *UserDao) Migrate() (int64, error) {
	return 1, dao.db.AutoMigrate(new(models.User))
}

func (dao *UserDao) Use(db *gorm.DB) interfaces.ItfUser {
	return &UserDao{db: db}
}

func (dao *UserDao) DB() *gorm.DB {
	return dao.db
}
